package com.lcifn.middleware.rpc.client;

import com.itranswarp.compiler.JavaStringCompiler;
import com.lcifn.middleware.rpc.client.stub.Stub;
import com.lcifn.middleware.rpc.transport.Transport;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * 动态桩工厂实现
 * @author lichao
 * @date 2021/1/31 14:30
 */
public class DynamicStubFactory implements StubFactory {

    private static final String STUB_CLASS_TEMPLATE =
            "package com.lcifn.middleware.rpc.client.stub;\n" +
                    "import com.lcifn.middleware.rpc.serializer.SerializerSupport;\n" +
                    "\n" +
                    "public class %s extends AbstractStub implements %s {\n" +
                    "%s\n" +
                    "}";

    public static final String STUB_METHOD_TEMPLATE =
            "    @Override\n" +
                    "    public %s %s(%s) {\n" +
                    "        Class[] parameterTypeClassList = {%s};\n" +
                    "        Object[] parameterTypeObjectList = {%s};\n" +
                    "        return SerializerSupport.deserialize(\n" +
                    "                invokeRemote(\n" +
                    "                        new RpcRequest(\n" +
                    "                                \"%s\",\n" +
                    "                                \"%s\",\n" +
                    "                                SerializerSupport.serialize(parameterTypeClassList),\n" +
                    "                                SerializerSupport.serialize(parameterTypeObjectList)\n" +
                    "                        )\n" +
                    "                ),\n" +
                    "        %s.class);\n" +
                    "    }\n";

    /**
     * 创建远程服务桩
     *
     * @param transport
     * @param serviceClass
     * @return T
     * @author lichao 2021/1/31 14:27
     */
    @Override
    public <T> T createStub(Transport transport, Class<T> serviceClass) {
        try {
            // 填充模板
            String stubSimpleName = serviceClass.getSimpleName() + "Stub";
            String classFullName = serviceClass.getName();
            String stubFullName = "com.lcifn.middleware.rpc.client.stub." + stubSimpleName;
            // String methodName = serviceClass.getMethods()[0].getName();

            StringBuilder methodStr = new StringBuilder();
            Method[] methods = serviceClass.getMethods();
            for (Method method : methods) {
                String methodReturnName = method.getReturnType().getName();
                String methodName = method.getName();
                StringBuilder parameterStr = new StringBuilder();
                StringBuilder parameterTypeClassStr = new StringBuilder();
                StringBuilder parameterTypeObjectStr = new StringBuilder();

                Class<?>[] parameterTypes = method.getParameterTypes();
                for (int i = 0; i < parameterTypes.length; i++) {
                    Class<?> parameterType = parameterTypes[i];
                    String parameterTypeName = parameterType.getName();
                    parameterStr.append(parameterTypeName + " arg" + i + ",");
                    parameterTypeClassStr.append("arg" + i + ".getClass()" + ",");
                    parameterTypeObjectStr.append("arg" + i + ",");
                }

                parameterStr.deleteCharAt(parameterStr.length() - 1);
                parameterTypeClassStr.deleteCharAt(parameterTypeClassStr.length() - 1);
                parameterTypeObjectStr.deleteCharAt(parameterTypeObjectStr.length() - 1);

                String methodSource = String.format(STUB_METHOD_TEMPLATE, methodReturnName, methodName, parameterStr.toString(),
                        parameterTypeClassStr.toString(), parameterTypeObjectStr.toString(),classFullName, methodName, methodReturnName);
                methodStr.append(methodSource);
            }

            String source = String.format(STUB_CLASS_TEMPLATE, stubSimpleName, classFullName, methodStr.toString());
            System.out.println("---------\n" + source + "----------\n");
            // 编译源代码
            JavaStringCompiler compiler = new JavaStringCompiler();
            Map<String, byte[]> results = compiler.compile(stubSimpleName + ".java", source);
            // 加载编译好的类
            Class<?> clazz = compiler.loadClass(stubFullName, results);

            // 把Transport赋值给桩
            Stub stubInstance = (Stub) clazz.newInstance();
            stubInstance.setTransport(transport);
            // 返回这个桩
            return (T) stubInstance;
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }
}
